// Go作者版权所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package ir

import (
	"cmd/compile/internal/base"
	"cmd/compile/internal/types"
	"cmd/internal/src"
)

// Decl是常量、类型或变量的声明。（声明的func是func。）
type Decl struct {
	miniNode
	X *Name // 正在申报的东西
}

func NewDecl(pos src.XPos, op Op, x *Name) *Decl {
	n := &Decl{X: x}
	n.pos = pos
	switch op {
	default:
		panic("invalid Decl op " + op.String())
	case ODCL, ODCLCONST, ODCLTYPE:
		n.op = op
	}
	return n
}

func (*Decl) isStmt() {}

// Stmt是可以显示为语句的节点。
// 这包括类似于语句的表达式，如f（）。
// None
// （它可能包括<-c，但这需要
// 将ORECV从UnaryExpr中分离出来，这还没有实现
// 必需的也许我们会在
// 有一点。）
type Stmt interface {
	Node
	isStmt()
}

// miniStmt是一个小型节点，具有语句所共有的额外字段。
type miniStmt struct {
	miniNode
	init Nodes
}

func (*miniStmt) isStmt() {}

func (n *miniStmt) Init() Nodes     { return n.init }
func (n *miniStmt) SetInit(x Nodes) { n.init = x }
func (n *miniStmt) PtrInit() *Nodes { return &n.init }

// AssignListStmt是带有
// 至少一侧有多个项目：Lhs=Rhs。
// 如果Def为true，则赋值为a:=。
type AssignListStmt struct {
	miniStmt
	Lhs Nodes
	Def bool
	Rhs Nodes
}

func NewAssignListStmt(pos src.XPos, op Op, lhs, rhs []Node) *AssignListStmt {
	n := &AssignListStmt{}
	n.pos = pos
	n.SetOp(op)
	n.Lhs = lhs
	n.Rhs = rhs
	return n
}

func (n *AssignListStmt) SetOp(op Op) {
	switch op {
	default:
		panic(n.no("SetOp " + op.String()))
	case OAS2, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV, OSELRECV2:
		n.op = op
	}
}

// AssignStmt是一个简单的赋值语句：X=Y。
// 如果Def为true，则赋值为a:=。
type AssignStmt struct {
	miniStmt
	X   Node
	Def bool
	Y   Node
}

func NewAssignStmt(pos src.XPos, x, y Node) *AssignStmt {
	n := &AssignStmt{X: x, Y: y}
	n.pos = pos
	n.op = OAS
	return n
}

func (n *AssignStmt) SetOp(op Op) {
	switch op {
	default:
		panic(n.no("SetOp " + op.String()))
	case OAS:
		n.op = op
	}
}

// assignoptstmt是AsOp=assignment语句：X AsOp=Y。
type AssignOpStmt struct {
	miniStmt
	X      Node
	AsOp   Op // OADD等
	Y      Node
	IncDec bool // 实际上是++或--
}

func NewAssignOpStmt(pos src.XPos, asOp Op, x, y Node) *AssignOpStmt {
	n := &AssignOpStmt{AsOp: asOp, X: x, Y: y}
	n.pos = pos
	n.op = OASOP
	return n
}

// BlockStmt是块：{List}。
type BlockStmt struct {
	miniStmt
	List Nodes
}

func NewBlockStmt(pos src.XPos, list []Node) *BlockStmt {
	n := &BlockStmt{}
	n.pos = pos
	if !pos.IsKnown() {
		n.pos = base.Pos
		if len(list) > 0 {
			n.pos = list[0].Pos()
		}
	}
	n.op = OBLOCK
	n.List = list
	return n
}

// BranchStmt是break、continue、fallthrough或goto语句。
type BranchStmt struct {
	miniStmt
	Label *types.Sym // 标签（如有）
}

func NewBranchStmt(pos src.XPos, op Op, label *types.Sym) *BranchStmt {
	switch op {
	case OBREAK, OCONTINUE, OFALL, OGOTO:
		// 好啊
	default:
		panic("NewBranch " + op.String())
	}
	n := &BranchStmt{Label: label}
	n.pos = pos
	n.op = op
	return n
}

func (n *BranchStmt) Sym() *types.Sym { return n.Label }

// case子句是开关或select:case List:Body中的case语句。
type CaseClause struct {
	miniStmt
	Var  *Name // 在类型开关中为此情况声明的变量
	List Nodes // 开关的表达式列表，早期选择
	Body Nodes
}

func NewCaseStmt(pos src.XPos, list, body []Node) *CaseClause {
	n := &CaseClause{List: list, Body: body}
	n.pos = pos
	n.op = OCASE
	return n
}

type CommClause struct {
	miniStmt
	Comm Node // 通信案例
	Body Nodes
}

func NewCommStmt(pos src.XPos, comm Node, body []Node) *CommClause {
	n := &CommClause{Comm: comm, Body: body}
	n.pos = pos
	n.op = OCASE
	return n
}

// ForStmt是非范围for循环：for Init；康德；Post{Body}
// Op可以是OFOR或OFORUNTIL（！Cond）。
type ForStmt struct {
	miniStmt
	Label    *types.Sym
	Cond     Node
	Late     Nodes
	Post     Node
	Body     Nodes
	HasBreak bool
}

func NewForStmt(pos src.XPos, init Node, cond, post Node, body []Node) *ForStmt {
	n := &ForStmt{Cond: cond, Post: post}
	n.pos = pos
	n.op = OFOR
	if init != nil {
		n.init = []Node{init}
	}
	n.Body = body
	return n
}

func (n *ForStmt) SetOp(op Op) {
	if op != OFOR && op != OFORUNTIL {
		panic(n.no("SetOp " + op.String()))
	}
	n.op = op
}

// GoDeferStmt是go或defer语句：go Call/defer Call。
// None
// 这两个操作码使用单一语法，因为实现
// 两者非常相似：都关心保存呼叫并运行它
// 在不同的上下文中（单独的goroutine或稍后的时间）。
type GoDeferStmt struct {
	miniStmt
	Call Node
}

func NewGoDeferStmt(pos src.XPos, op Op, call Node) *GoDeferStmt {
	n := &GoDeferStmt{Call: call}
	n.pos = pos
	switch op {
	case ODEFER, OGO:
		n.op = op
	default:
		panic("NewGoDeferStmt " + op.String())
	}
	return n
}

// IfStmt是一个返回语句：if Init；Cond{Then}else{else}。
type IfStmt struct {
	miniStmt
	Cond   Node
	Body   Nodes
	Else   Nodes
	Likely bool // 代码布局提示
}

func NewIfStmt(pos src.XPos, cond Node, body, els []Node) *IfStmt {
	n := &IfStmt{Cond: cond}
	n.pos = pos
	n.op = OIF
	n.Body = body
	n.Else = els
	return n
}

// InlineMarkStmt是放置在内联主体之前的标记。
type InlineMarkStmt struct {
	miniStmt
	Index int64
}

func NewInlineMarkStmt(pos src.XPos, index int64) *InlineMarkStmt {
	n := &InlineMarkStmt{Index: index}
	n.pos = pos
	n.op = OINLMARK
	return n
}

func (n *InlineMarkStmt) Offset() int64     { return n.Index }
func (n *InlineMarkStmt) SetOffset(x int64) { n.Index = x }

// LabelStmt是一个label语句（只是label，不包括它所标记的语句）。
type LabelStmt struct {
	miniStmt
	Label *types.Sym // “标签：”
}

func NewLabelStmt(pos src.XPos, label *types.Sym) *LabelStmt {
	n := &LabelStmt{Label: label}
	n.pos = pos
	n.op = OLABEL
	return n
}

func (n *LabelStmt) Sym() *types.Sym { return n.Label }

// RangeSMT是一个范围循环：对于键，Value=range X{Body}
type RangeStmt struct {
	miniStmt
	Label    *types.Sym
	Def      bool
	X        Node
	Key      Node
	Value    Node
	Body     Nodes
	HasBreak bool
	Prealloc *Name
}

func NewRangeStmt(pos src.XPos, key, value, x Node, body []Node) *RangeStmt {
	n := &RangeStmt{X: x, Key: key, Value: value}
	n.pos = pos
	n.op = ORANGE
	n.Body = body
	return n
}

// ReturnStmt是一个return语句。
type ReturnStmt struct {
	miniStmt
	origNode       // 对于typecheckargs重写
	Results  Nodes // 返回列表
}

func NewReturnStmt(pos src.XPos, results []Node) *ReturnStmt {
	n := &ReturnStmt{}
	n.pos = pos
	n.op = ORETURN
	n.orig = n
	n.Results = results
	return n
}

// SelectStmt是一个块：{Cases}。
type SelectStmt struct {
	miniStmt
	Label    *types.Sym
	Cases    []*CommClause
	HasBreak bool

	// TODO（rsc）：不是在这里录制，而是用块替换？
	Compiled Nodes // 已编译表单，在walkSwitch之后
}

func NewSelectStmt(pos src.XPos, cases []*CommClause) *SelectStmt {
	n := &SelectStmt{Cases: cases}
	n.pos = pos
	n.op = OSELECT
	return n
}

// sendsmt是一个send语句：X<-Y。
type SendStmt struct {
	miniStmt
	Chan  Node
	Value Node
}

func NewSendStmt(pos src.XPos, ch, value Node) *SendStmt {
	n := &SendStmt{Chan: ch, Value: value}
	n.pos = pos
	n.op = OSEND
	return n
}

// SwitchStmt是一个switch语句：switch Init；Expr{Cases}。
type SwitchStmt struct {
	miniStmt
	Tag      Node
	Cases    []*CaseClause
	Label    *types.Sym
	HasBreak bool

	// TODO（rsc）：不是在这里录制，而是用块替换？
	Compiled Nodes // 已编译表单，在walkSwitch之后
}

func NewSwitchStmt(pos src.XPos, tag Node, cases []*CaseClause) *SwitchStmt {
	n := &SwitchStmt{Tag: tag, Cases: cases}
	n.pos = pos
	n.op = OSWITCH
	return n
}

// TailCallStmt是一个tail-call语句，用于后端调用
// 直接跳转到另一个函数的代码生成。
type TailCallStmt struct {
	miniStmt
	Target *Name
}

func NewTailCallStmt(pos src.XPos, target *Name) *TailCallStmt {
	if target.Op() != ONAME || target.Class != PFUNC {
		base.FatalfAt(pos, "tail call to non-func %v", target)
	}
	n := &TailCallStmt{Target: target}
	n.pos = pos
	n.op = OTAILCALL
	return n
}

// TypeSwitchGuard是类型开关中的[Name:=]X.（类型）。
type TypeSwitchGuard struct {
	miniNode
	Tag  *Ident
	X    Node
	Used bool
}

func NewTypeSwitchGuard(pos src.XPos, tag *Ident, x Node) *TypeSwitchGuard {
	n := &TypeSwitchGuard{Tag: tag, X: x}
	n.pos = pos
	n.op = OTYPESW
	return n
}
